"""
Conjunto de metodos para hacer clustering. Todos los metodos
devielven una lista de Blobs

labelClustering = realiza una clusterizacion basada en labeling

Autor: Gonzalo Luzardo Morocho
gluzardo@espol.edu.ec, gonchalox@gmail.com
"""

from Blob import *
from Imagex import *

NOT_LABELED = -1
NO_ACTIVE = 0
ACTIVE = 1
HETEROGENEUS_NEIGHBORHOOD = -2

# Label clustering
def labelClustering(binaryImage):
	blobList = []
	#Obtenemos los datos. La matriz se transpone para ajustar a las mismas
	#coordenadas de la imagen
	dataLabeled = array(zeros([binaryImage._height,binaryImage._width]))
	dataLabeled[:,:] = binaryImage._array[:,:]
	dataLabeled = dataLabeled.transpose()
	#dataLabeled.dtype = int
	getActivePixels(dataLabeled)
	labeling(dataLabeled)
	width, height = dataLabeled.shape
	
	#Creamos los blobs
	for y in range(0,height):
		for x in range(0,width):
			#Buscamos las etiquetas colocadas, que son diferentes de NO_ACTIVE
			if(dataLabeled[x][y] != NO_ACTIVE):
				#Miramos si existe un blob identificado con dicho label en la lista
				findit = False
				for b in range(0,len(blobList)):
					#Si existe un blob con ese Id agregamos el punto al blob
					if(blobList[b]._id == dataLabeled[x][y]):
						blobList[b].addPoint(x,y)
						findit = True
						break
				#Si llega al final quiere decir que no hay un blob con dicho id
				if(findit == False):
				#Creamos un blob y lo agregamos a la lista
					blobList.append(Blob(x,y,dataLabeled[x][y]))
				
	return [dataLabeled, blobList]

def getActivePixels(matrix):
	width, height = matrix.shape

	for y in range(0,height):
		for x in range(0,width):
			if(x==0 or x==width-1 or y==0 or y==height-1):
				matrix[x][y] = NO_ACTIVE
			elif(int(matrix[x][y]) == int(COLOR_MAX_VALUE)):
				matrix[x][y] = NOT_LABELED
			else:
				matrix[x][y] = NO_ACTIVE
	
def printMatrix(matrix):
	width, height = matrix.shape
	line = ""
	for y in range(0,height):
		for x in range(0,width):
			line = line + " " + str(matrix[x][y])
		print(line)
		line = ""
 
def getMin(matrix):
	width, height = matrix.shape
	m = matrix[0][0]
	for y in range(0,height):
		for x in range(0,width):
			if(matrix[x][y] < m):
				m = matrix[x][y]
	return m
		
# Retorna el identificador del vecindario
# Casos especiales:
# HETEROGENEUS_NEIGHBORHOOD si alguno de sus vecinos es diferente
# NOT_LABELED si todos sus vecinos no estan etiquetados
# NO_ACTIVE si no tiene vecinos
def getNeighborhoodLabel(label_matrix, px, py):
	x=0
	y=0
	last_neighbour = NOT_LABELED
	neighbour_counter = 0
	for i in range(0,9):
		if(label_matrix[px+x-1][py+y-1] != NO_ACTIVE and (x!=1 or y!=1)):
			neighbour_counter = neighbour_counter + 1	
			if(label_matrix[px+x-1][py+y-1] != NOT_LABELED and (x!=1 or y!=1)):
				if(last_neighbour == NOT_LABELED):		
					last_neighbour = label_matrix[px+x-1][py+y-1]
				else:
					if(last_neighbour != label_matrix[px+x-1][py+y-1]):
						return HETEROGENEUS_NEIGHBORHOOD				
		x=(x+1)%3
		if(x==0):
			y=y+1
	if(neighbour_counter > 0):		
		return last_neighbour
	else:
		return 	NO_ACTIVE

# Renonmbrado de etiquetas hacia atras
# Renombra todas las etiquetas desde px,py hacia atras
# cambiando el valor usando label_list
def backChangeLabel(label_matrix, px, py, label_list):
	#print("\nRealizando el BackLabeling...")
	width, height = label_matrix.shape
	
	#print("Cambiar: px:" + str(px) + " py:" + str(py) + " Lista recibida : " + str(label_list)) 
	
	if(len(label_list) < 2):
		#print("Lista muy corta")
		return 0
	dominant_label = label_list[0]
	dominated_labels = label_list[1:len(label_list)]
	
	#print("Cambiando a: " + str(dominant_label) + " la lista:")
	#print(dominated_labels)   
	
	#En toda la matriz	
	for y in range(0,height):
		for x in range(0,width):
			#Solo trabajamos con pixeles no activos o no etiquetados
			if(label_matrix[x][y] != NO_ACTIVE and label_matrix[x][y] != NOT_LABELED):
					#Si no buscamos si es un pixel que debemos cambiar para luego hacer el cambio
					for l in range(0,len(dominated_labels)):
						if(label_matrix[x][y] == dominated_labels[l]):
							#print("Encontrado: " + str(dominated_labels[l]) + " en x: " + str(x) + "  y: " + str(y) + " Cambiando a: " + str(dominant_label))
							label_matrix[x][y] = dominant_label
							break
					
					#Si llegamos a la meta salimos de la funcion
					if(x==px and y==py):
						#print "Llegamos x:" + str(x) + " y:" + str(y) + " px:" + str(px) + " py:" + str(py)
						return 0
	#print("No se econtro nada\n")				
	
# Obtiene las etiquetas de todos sus vecinos
# Devuelve una lista de etiquetas de todos sus vecinos
def getNeighborhoodList(label_matrix, px, py):
	x=0
	y=0
	neighborhood = []
	for i in range(0,9):
		if(label_matrix[px+x-1][py+y-1] != NO_ACTIVE):
			if (label_matrix[px+x-1][py+y-1] != NOT_LABELED and (x!=1 or y!=1)):
				if((label_matrix[px+x-1][py+y-1] in neighborhood) == False):
					neighborhood.append(label_matrix[px+x-1][py+y-1])
		x=(x+1)%3
		if(x==0):
			y=y+1
	return neighborhood

# Establece la etiqueta (label) de los vecinos no etiquetados
def setNeighborhoodLabel(label_matrix, px, py, label):
	x=0
	y=0
	for i in range(0,9):
		if(label_matrix[px+x-1][py+y-1] != NO_ACTIVE):
			if (label_matrix[px+x-1][py+y-1] == NOT_LABELED and (x!=1 or y!=1)):
				label_matrix[px+x-1][py+y-1] = label
		x=(x+1)%3
		if(x==0):
			y=y+1
						

#Procedimiento de labeling
def labeling(label_matrix):
	width, height = label_matrix.shape
	count_label = 1
	
	print("Iniciando Labeling...");
	
	#Recorremos toda la matriz excepto los bordes
	for y in range(1,height-1):
		for x in range(1,width-1):
			
			#Si no tiene etiqueta
			if(label_matrix[x][y] == NOT_LABELED):
				
				ret_n = getNeighborhoodLabel(label_matrix,x,y)
				#print ("\nNo tiene etiqueta: x:" + str(x) + " y:" + str(y) + " Label: "+ str(label_matrix[x][y]) + " Ret: " + str(ret_n))
				
				###
				#print("\nPrevio:");
				#printMatrix(matrix)
				###
				
				#Si todos sus vecinos no estan etiquetados o no tiene vecinos
				if(ret_n == NOT_LABELED or ret_n == NO_ACTIVE):
					#print("Todos sus vecinos no estan etiquetados o no tiene vecinos");
					#Se marca el pixel con la etiqueta siguiente
					label_matrix[x][y] = count_label
					#Se marcan todos los vecinos no etiquetados con la misma etiqueta
					setNeighborhoodLabel(label_matrix,x,y,count_label)
					#Incrementamos el valor de la etiqueta para el siguiente label	
					count_label = count_label + 1
				
				#Si todos los vecinos etiquetados son diferentes
				elif(ret_n == HETEROGENEUS_NEIGHBORHOOD):
					#print("\nRenombrado de etiquetas si la vecindad es heterogenea " + str(x) + " " + str(y) + " Label: " + str(label_matrix[x][y]));
					#Renombrado de etiquetas si la vecindad es heterogenea
					#Se obtiene la lista de vecinos presentes en la vecindad
					n_label = getNeighborhoodList(label_matrix, x, y)
										
					#Se ordena la lista
					n_label.sort()
					
					#print(n_label)
					
					#Se procede a hacer el cambio de etiquetas
					backChangeLabel(label_matrix, x, y, n_label)
									
				#Si alguno de sus vecinos esta etiquetado
				else:
					#print("Alguno de sus vecinos esta etiquetado");
					#Se marca el pixel con la etiqueta de sus vecinos
					label_matrix[x][y] = ret_n
					#Se marcan todos los vecinos no etiquetados con la misma etiqueta
					setNeighborhoodLabel(label_matrix,x,y,ret_n)
					
				###
				#print("\nPaso: Label: " + str(count_label))
				#printMatrix(matrix)
				###	
			
			#Si tiene una etiqueta		
			elif(label_matrix[x][y] != NO_ACTIVE):
				#print("\nTiene etiqueta: x:" + str(x) + " y:" + str(y) + " Label: "+ str(label_matrix[x][y]))
				ret_n = getNeighborhoodLabel(label_matrix,x,y)	
				
				
				###
				##print("\nPrevio:");
				##printMatrix(matrix)
				###
				
				#Si todos los vecinos etiquetados tienen la misma etiqueta
				if(ret_n != HETEROGENEUS_NEIGHBORHOOD): #Si la vecindad no es heterogenea
					#print("Todos los vecinos etiquetados tienen la misma etiqueta");
					#Se marcan los vecinos no etiquetados con dicha etiqueta
					setNeighborhoodLabel(label_matrix,x,y,ret_n)
				
				else:
					#print("Renombrado de etiquetas si la vecindad es heterogenea");
					#Renombrado de etiquetas si la vecindad es heterogenea
					#Se obtiene la lista de vecinos presentes en la vecindad
					n_label = getNeighborhoodList(label_matrix, x, y)
										
					#Se ordena la lista
					n_label.sort()
					
					if n_label[0] == -2:
						return 0
					
					#Se procede a hacer el cambio de etiquetas
					backChangeLabel(label_matrix, x, y, n_label)
			
				###
				##print("\nPaso: Label: " + str(count_label))
				##printMatrix(matrix)
				###
	print("Labeling finalizado, ultimo tag: " + str(count_label));
